/*
 * Copyright (C) 2012 haru Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.harusource.di.delegate;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.harusource.di.core.HaruCore;

import android.util.SparseArray;
import android.view.View;

public abstract class Delegate {
    public static final int EVENTHANDLE_DEFAULT = 1;
    
    protected WeakReference<Object> mTarget = new WeakReference<Object>(null);

    protected SparseArray<Method> mEventHandles = new SparseArray<Method>();

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    protected @interface EventHandler {
        int value() default Delegate.EVENTHANDLE_DEFAULT;
    }
    
    public void add(Method eventtemplate, Object target, Method eventhandle, View view) {
        if (target == null || eventhandle == null)
            throw new RuntimeException();

        // Set the eventhandle accessible
        eventhandle.setAccessible(true);

        mTarget = new WeakReference<Object>(target);

        //
        int handleid = ((EventHandler) eventtemplate.getAnnotation(EventHandler.class)).value();
        mEventHandles.append(handleid, eventhandle);
    }

    public static Delegate getDelegateInstanceFromCache(Class<?> delegate_type,
            SparseArray<Map<Class<?>, Delegate>> cache, int resourceid) {
        try {
            if(cache == null)
                return (Delegate) delegate_type.newInstance();
            
            Map<Class<?>, Delegate> objects = cache.get(resourceid);
            Delegate delegate = null;

            // no registered delegate instances
            // create a cache for resouce id
            if (objects == null) {
                objects = new HashMap<Class<?>, Delegate>();
                cache.append(resourceid, objects);
            }

            // no delegate related with resouce id
            delegate = objects.get(delegate_type);
            if (delegate == null){
                delegate = (Delegate) delegate_type.newInstance();
                objects.put(delegate_type, delegate);
            }

            return delegate;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Method[] findEventHandler(Class<?> clazz) {
        List<Method> eventhandles = new ArrayList<Method>();
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {
            Annotation[] annotations = method.getDeclaredAnnotations();
            for (Annotation annotaion : annotations) {
                if (annotaion instanceof EventHandler)
                    eventhandles.add(method);
            }
        }
        if (eventhandles.size() == 0)
            throw new RuntimeException(HaruCore.ERROR_MSG3);

        return eventhandles.toArray(new Method[0]);
    }

    public static Method findEventHandler(Class<?> clazz, Method eventhandle){
        Method[] methods = findEventHandler(clazz);
        
        for(Method method : methods){
            if(HaruCore.compareMethod(method, eventhandle) == true)
                return method;
        }
        return null;
    }
    
    public static Object invoke0(WeakReference<Object> _target, SparseArray<Method> _handlers, int method_id){
        try{
            Object target = _target.get();
            Method handle = _handlers.get(method_id);
            if (target != null && handle != null)
                return handle.invoke(target);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return null;
    }
    
    public static <T0> Object invoke1(WeakReference<Object> _target, SparseArray<Method> _handlers, int method_id, T0 arg0){
        try{
            Object target = _target.get();
            Method handle = _handlers.get(method_id);
            if (target != null && handle != null)
                return handle.invoke(target, arg0);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return null;
    }

    public static <T0, T1> Object invoke2(WeakReference<Object> _target, SparseArray<Method> _handlers, int method_id, T0 arg0, T1 arg1){
        try{
            Object target = _target.get();
            Method handle = _handlers.get(method_id);
            if (target != null && handle != null)
                return handle.invoke(target, arg0, arg1);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return null;
    }
    
    public static <T0, T1, T2> Object invoke3(WeakReference<Object> _target, SparseArray<Method> _handlers, int method_id, T0 arg0, T1 arg1, T2 arg2){
        try{
            Object target = _target.get();
            Method handle = _handlers.get(method_id);
            if (target != null && handle != null)
                return handle.invoke(target, arg0, arg1, arg2);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return null;
    }
    
    public static <T0, T1, T2, T3> Object invoke4(WeakReference<Object> _target, SparseArray<Method> _handlers, int method_id, T0 arg0, T1 arg1, T2 arg2, T3 arg3){
        try{
            Object target = _target.get();
            Method handle = _handlers.get(method_id);
            if (target != null && handle != null)
                return handle.invoke(target, arg0, arg1, arg2, arg3);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return null;
    }
    
    public static <T0, T1, T2, T3, T4> Object invoke5(WeakReference<Object> _target, SparseArray<Method> _handlers, int method_id, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4){
        try{
            Object target = _target.get();
            Method handle = _handlers.get(method_id);
            if (target != null && handle != null)
                return handle.invoke(target, arg0, arg1, arg2, arg3, arg4);
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return null;
    }
}
